import pyttsx3
import wave
import sounddevice as sd
import numpy as np
import tempfile
import threading
import os
from typing import Self

from sympy import false


class TTSDeviced:

    def __init__(self) -> None:
        self.__engine = None
        self._lock = threading.RLock()
        self._thread_started = False

    @property
    def rate(self) -> int:
        """语速 i.e. 150"""
        rate = self._engine.getProperty("rate")
        assert isinstance(rate, int), "Rate should be an integer"
        return rate

    @rate.setter
    def rate(self, value: int) -> None:
        self._engine.setProperty("rate", value)

    @property
    def volume(self) -> float:
        """音量 i.e. 1.0"""
        volume = self._engine.getProperty("volume")
        assert isinstance(volume, float), "Volume should be a float"
        return volume

    @volume.setter
    def volume(self, value: float) -> None:
        self._engine.setProperty("volume", value)

    @property
    def _engine(self) -> pyttsx3.Engine:
        engine = self.__engine
        if engine is None:
            self.__engine = engine = pyttsx3.init()
        return engine

    def start(self) -> None:
        """
        启动 TTS 引擎
        """
        if self._thread_started:
            self.stop()
        thread = threading.Thread(target=self._engine.startLoop)
        thread.start()
        self._thread_started = True

    def tts_to_device(self, text: str, *, device: int | str):
        """
        使用 pyttsx3 生成语音，通过 sounddevice 播放到指定声卡

        :param text: 要朗读的文本
        :param device: sounddevice 设备索引或名称
        :param rate: 语速
        :param volume: 音量 (0.0 ~ 1.0)
        """
        with self._lock:
            self._tts_to_device(text, device=device)

    def _tts_to_device(self, text: str, *, device: int | str):
        # 创建临时文件
        with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tmpfile:
            temp_filename = tmpfile.name

        try:
            engine = self._engine

            # 生成语音并保存到临时文件
            engine.save_to_file(text, temp_filename)

            # 读取生成的 WAV 文件
            with wave.open(temp_filename, "rb") as wf:
                # 获取音频参数
                channels = wf.getnchannels()
                sampwidth = wf.getsampwidth()  # 通常是 2 (16-bit)
                framerate = wf.getframerate()
                nframes = wf.getnframes()

                # 读取所有音频数据
                raw_data = wf.readframes(nframes)

            # 将原始字节数据转为 numpy 数组
            if sampwidth == 1:
                dtype = np.uint8  # 8-bit
            elif sampwidth == 2:
                dtype = np.int16  # 16-bit
            elif sampwidth == 4:
                dtype = np.int32
            else:
                raise ValueError(f"Unsupported sample width: {sampwidth}")

            audio = np.frombuffer(raw_data, dtype=dtype)

            # 如果是立体声，reshape 成 (N, 2)
            if channels == 2:
                audio = audio.reshape(-1, 2)
            elif channels == 1:
                audio = audio  # 单声道

            # 使用 sounddevice 播放
            sd.play(audio, samplerate=framerate, device=device)
            sd.wait()  # 等待播放完成

        finally:
            # 清理临时文件
            if os.path.exists(temp_filename):
                os.unlink(temp_filename)

    def stop(self) -> None:
        """
        停止当前的语音播放
        """
        if self._thread_started:
            self._engine.endLoop()
            self._thread_started = false

    def __enter__(self) -> Self:
        """
        进入上下文管理器，启动 TTS 引擎
        """
        self.start()
        return self

    def __exit__(self, exc_type, exc_value, traceback) -> None:
        """
        退出上下文管理器，停止 TTS 引擎
        """
        self.stop()

    @staticmethod
    def list_devices() -> list[str]:
        """
        列出所有可用的音频设备。
        @return: 可用设备名称列表
        """
        devices = sd.query_devices()

        def get_name(device) -> str:
            return device["name"]

        return [get_name(device) for device in devices]


if __name__ == "__main__":

    # LIST = True
    LIST = False

    if LIST:
        devices = TTSDeviced.list_devices()  # 列出可用设备
        print("可用音频设备:")
        for i, device in enumerate(devices):
            print(f"{i}: {device}")
        print("请在上面的列表中选择设备索引。")

    else:
        tts = TTSDeviced()
        tts.start()
        print("TTS 引擎已启动。")
        tts.rate = 150  # 设置语速
        tts.volume = 1.0  # 设置音量

        device = 21

        # 示例：播放到默认设备
        tts.tts_to_device(
            "你好",
            device=device,
        )
        tts.tts_to_device(
            "世界",
            device=device,
        )
